package nborm;

import android.database.Cursor;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by YuanChao on 18-12-24.
 */
class QueryUtils {
    private static final String TAG = "Query";
    private static final ConcurrentHashMap<Class<?>, Pair<Constructor<?>, Object[]>> pairConcurrentHashMap = new ConcurrentHashMap<>();

    /**
     * 映射
     *
     * @param cursor 游标
     * @return 对象
     */
    public static <T> T toObj(Cursor cursor, TableInfo mTableInfo, Class<T> mTClass) {
        T object = null;
        try {
            if (pairConcurrentHashMap.containsKey(mTClass)) {
                Log.i(TAG, "toObj: get constructor from cache");
                Pair<Constructor<?>, Object[]> constructorPair = pairConcurrentHashMap.get(mTClass);
                object = (T) constructorPair.first.newInstance(constructorPair.second);
            }
            Constructor<T>[] constructors = (Constructor<T>[]) mTClass.getConstructors();
            if (hasDefConstructor(constructors)) {
                object = mTClass.newInstance();
                pairConcurrentHashMap.put(mTClass, new Pair<Constructor<?>, Object[]>(constructors[0], new Object[]{}));
            } else {
                Pair<Constructor<T>, Object[]> constructorPair = getSupportConstructor(constructors);
                if (constructorPair == null) {
                    return null;
                }
                pairConcurrentHashMap.put(mTClass, new Pair<Constructor<?>, Object[]>(constructorPair.first, constructorPair.second));
                object = constructorPair.first.newInstance(constructorPair.second);
            }
        } catch (Exception e) {
            Log.e(TAG, String.valueOf(e)
                    + ",对于java来说你应该声明一个默认的无参的构造函数");
        }
        for (Map.Entry<String, Field> entry : mTableInfo.getColumns().entrySet()) {
            try {
                int index = cursor.getColumnIndex(entry.getKey());
                Class<?> fieldType = entry.getValue().getType();
                Object value = null;
                if (TableHelper.isCustomField(entry.getValue())) {
                    value = DBHelper.get().getTypeConverter().toObject(entry.getValue().getGenericType(), cursor.getString(index));
                } else if (fieldType == String.class) {
                    //字符串类型
                    value = cursor.getString(index);
                } else if (TableHelper.isNumber(fieldType)) {
                    //浮点型
                    if (fieldType == float.class
                            || fieldType == Float.class) {
                        value = cursor.getFloat(index);
                    } else if (fieldType == double.class
                            || fieldType == Double.class) {
                        value = cursor.getDouble(index);
                    } else if (fieldType == Integer.class || fieldType == int.class) {
                        //整型
                        value = cursor.getInt(index);
                    } else if (fieldType == Long.class || fieldType == long.class) {
                        //长整型
                        value = cursor.getLong(index);
                    } else if (fieldType == short.class || fieldType == Short.class) {
                        value = cursor.getShort(index);
                    }
                } else if (fieldType == Boolean.class || fieldType == boolean.class) {
                    //布尔型
                    value = "true".equals(cursor.getString(index));
                } else if (fieldType == char.class || fieldType == Character.class) {
                    String v = cursor.getString(index);
                    if (!TextUtils.isEmpty(v)) {
                        value = v.charAt(0);
                    }
                } else {
                    // 斩不支持二进制
                    Log.d(TAG, "type not support");
                }
                entry.getValue().set(object, value);
            } catch (Exception e) {
                Log.e(TAG, String.valueOf(e));
            }
        }
        return object;
    }

    private static boolean hasDefConstructor(Constructor[] constructors) {
        if (constructors == null || constructors.length == 0) {
            return true;
        }
        for (Constructor constructor : constructors) {
            if (constructor.getParameterTypes().length == 0) {
                Log.i(TAG, "hasDefConstructor: find default constructor!");
                return true;
            }
        }
        return false;
    }

    private static <T> Pair<Constructor<T>, Object[]> getSupportConstructor(Constructor<T>[] constructors) {
        if (constructors == null || constructors.length == 0) {
            return null;
        }
        for (Constructor<T> constructor : constructors) {
            if (isBasicParams(constructor.getParameterTypes())) {
                Log.i(TAG, "getSupportConstructor: have basic types constructor");
                Object[] values = new Object[constructor.getParameterTypes().length];
                for (int i = 0; i < constructor.getParameterTypes().length; i++) {
                    values[i] = getDefValue(constructor.getParameterTypes()[i]);
                }
                return new Pair<>(constructor, values);
            }
        }
        return null;
    }

    private static boolean isBasicParams(Class<?>[] classes) {
        if (classes == null || classes.length == 0)
            return false;
        for (Class<?> aClass : classes) {
            if (!isBasicOrStrType(aClass)) {
                return false;
            }
        }
        return true;
    }

    private static boolean isBasicOrStrType(Class<?> cls) {
        return cls == String.class
                || cls == int.class
                || cls == long.class
                || cls == byte.class
                || cls == short.class
                || cls == double.class
                || cls == float.class
                || cls == boolean.class
                || cls == char.class;
    }

    private static Object getDefValue(Class<?> cls) {
        if (cls == String.class) {
            return "";
        }
        if (cls == int.class) {
            return 0;
        }
        if (cls == long.class) {
            return 0L;
        }
        if (cls == byte.class) {
            return 0;
        }
        if (cls == short.class) {
            return 0;
        }
        if (cls == double.class) {
            return 0;
        }
        if (cls == float.class) {
            return 0F;
        }
        if (cls == boolean.class) {
            return false;
        }
        if (cls == char.class) {
            return 0;
        }
        return null;
    }
}
